<template>
  <div :id="boxId"
       class="right"
       :style="{width:option.width||'100%',height:option.height||'608px','-webkit-transform': `scale(${scale})`,'-moz-transform': `scale(${scale})`,'-ms-transform': `scale(${scale})`,'-o-transform': `scale(${scale})`,'transform': `scale(${scale})`}"
       @mousedown="clickFlag = true"
       @mouseup="clickFlag = false"
       ref="flow">
    <node v-for="(node,index) in option.nodeList"
          :node="node"
          :id="node.id"
          :isDrag="isDrag"
          :isSelection="getSelection(index)"
          :isLast="lastNode.id === node.id"
          :limit="getLimit(index)"
          :scale="scale"
          :nodeMaintain="nodeMaintain"
          @changeNodeSite="changeNodeSite"
          @deleteNode="deleteNode"
          @clickNode="clickNode"
          :index="index"
          :key="index">
    </node>
  </div>
</template>

<script>
import node from './node'
import jsplumb from 'jsplumb'

export default {
  components: { node },
  props: {
    option: {
      type: Object,
      default: {}
    },
    nodeMaintain: {
      type: Boolean,
      default: false
    },
    simple: {
      type: Boolean,
      default: false
    },
    boxId: {
      type: String,
      default: 'container'
    },
    isDrag: {
      type: Boolean,
      default: true
    },
    lastNode: {
      type: Object,
      default: () => {
        return {}
      }
    },
    orgId: Number,
    limitNode: {
      type: Array,
      default: () => {
        return []
      }
    }
  },
  watch: {
    clickFlag () {
      console.log(this.clickFlag)
    },
    isDrag () {
      if (this.isDrag && !this.loadDrag) {
        this.option.nodeList.some(res => {
          this.jsPlumb.makeSource(`${res.id}`, this.jsplumbSourceOptions)
          this.jsPlumb.makeTarget(`${res.id}`, this.jsplumbTargetOptions)
          this.jsPlumb.draggable(`${res.id}`, { containment: 'parent', grid: [1, 1] })
        })
        this.loadDrag = true
      }
    }
  },
  data () {
    return {
      jsPlumb: {},
      // 默认设置参数
      jsplumbSetting: {
        Container: this.boxId,
        // 动态锚点、位置自适应
        Anchors: ['Top', 'TopCenter', 'TopRight', 'TopLeft', 'Right', 'RightMiddle', 'Bottom', 'BottomCenter', 'BottomRight', 'BottomLeft', 'Left', 'LeftMiddle'],
        // 连线的样式 StateMachine、Flowchart
        Connector: ['Flowchart', { curviness: 0 }],
        // 鼠标不能拖动删除线
        ConnectionsDetachable: false,
        // 删除线的时候节点不删除
        DeleteEndpointsOnDetach: false,
        // 连线的端点
        // Endpoint: ["Dot", {radius: 5}],
        Endpoint: ['Rectangle', { height: 10, width: 10 }],
        // 线端点的样式
        EndpointStyle: { fill: 'rgba(255,255,255,0)', outlineWidth: 1 },
        LogEnabled: true, // 是否打开jsPlumb的内部日志记录
        // 绘制线
        PaintStyle: { stroke: 'black', strokeWidth: 3 },
        // 绘制箭头
        Overlays: [['Arrow', { width: 12, length: 12, location: 1 }]],
        RenderMode: 'svg'
      },
      // jsplumb连接参数
      jsplumbConnectOptions: {
        isSource: true,
        isTarget: true,
        // 动态锚点、提供了4个方向 Continuous、AutoDefault
        anchor: 'Continuous'
      },
      jsplumbSourceOptions: {
        filter: '.avue-flow__node-drag', /* "span"表示标签，".className"表示类，"#id"表示元素id*/
        filterExclude: false,
        anchor: 'Continuous',
        allowLoopback: false
      },
      jsplumbTargetOptions: {
        filter: '.avue-flow__node-drag', /*"span"表示标签，".className"表示类，"#id"表示元素id */
        filterExclude: false,
        anchor: 'Continuous',
        allowLoopback: false
      },
      loadEasyFlowFinish: false,
      loopFlag: false,
      // 视图拖动缩放控制
      clickFlag: false,
      loadDrag: false,
      currentIndex: -1,
      scale: 1,
      // 历史线对象
      prevLineObj: {}
    }
  },
  mounted () {
    this.jsPlumb = jsPlumb.getInstance()
    this.$nextTick(() => {
      this.jsPlumbInit()
    })
    const dom = document.getElementById(this.boxId)

    /*注册事件*/
    if (document.addEventListener) {
      dom.addEventListener('DOMMouseScroll', this.scrollFunc, false);
    }//W3C 
    dom.onmousewheel = this.scrollFunc;//IE/Opera/Chrome 
  },
  methods: {
    scrollFunc (e) {
      if (!this.isDrag) return
      e.preventDefault()
      e = e || window.event
      const upDown = e.wheelDelta || e.detail
      if (upDown > 0) {
        if (this.scale >= 1.5) return
        this.scale += 0.02
      }
      else {
        if (this.scale <= 0.4) return
        this.scale -= 0.02
      }
      this.jsPlumb.setZoom(this.scale)
    },
    getLimit (index) {
      let result = false
      this.limitNode.some(res => {
        if (res.id === this.option.nodeList[index].id) {
          result = true
          return true
        }
      })
      return result
    },
    getSelection (index) {
      return this.currentIndex === index
    },
    clickNode (data) {
      this.$emit('clickNode', data.node)
      if (data.index === 0 || data.index === this.option.lineList.length) return
      this.currentIndex = data.index
    },
    // 是否具有该线
    hasLine (from, to) {
      for (var i = 0; i < this.option.lineList.length; i++) {
        var line = this.option.lineList[i]
        if (`${line.fromId}` === `${from}` && `${line.toId}` === `${to}`) return true
      }
      return false
    },
    // 是否回环
    hashOppositeLine (id, source) {
      this.option.lineList.some(res => {
        if (`${id}` === `${source}`) {
          this.loopFlag = true
          return true
        }
        if (`${id}` === `${res.fromId}`) {
          this.hashOppositeLine(res.toId, source)
          // return true
        }
      })
    },
    // 删除线
    deleteLine (from, to, id) {
      if (this.loadEasyFlowFinish && !this.simple)
        this.option.lineList = this.option.lineList.filter(function (line) {
          if (id) return `${line.id}` !== `${id}`
          return `${line.fromId}` !== `${from}` || `${line.toId}` !== `${to}`
        })
    },
    // 改变连线
    changeLine (oldFrom, oldTo) {
      this.deleteLine(oldFrom, oldTo)
    },
    // 改变节点的位置
    changeNodeSite ({ index, left, top }) {
      this.$emit('changeNodeSite', { index, left, top })
      for (var i = 0; i < this.option.nodeList.length; i++) {
        let node = this.option.nodeList[i]
        if (i === index) {
          this.$set(this.option.nodeList[i], 'leftCoord', left)
          this.$set(this.option.nodeList[i], 'topCoord', top)
        }
      }
    },
    //删除节点
    deleteNode (node) {
      if (this.simple) return
      this.$confirm(this.$t('processDialog.isDeleteNode') + node.nodeName + '?', this.$t('processDialog.tips'), {
        confirmButtonText: this.$t('submitText'),
        cancelButtonText: this.$t('cancelText'),
        type: 'warning',
        closeOnClickModal: false
      }).then(() => {
        const nextLineArr = [] // 下一个节点ID
        const prevLineArr = [] // 上一个节点ID
        this.option.lineList.some(res => {
          if (`${node.id}` === `${res.fromId}`) nextLineArr.push(res.toId)
          if (`${node.id}` === `${res.toId}`) prevLineArr.push(res)
        })
        if (nextLineArr.length > 1)
          this.$message.warning(this.$t('processDialog.cannotDelMultiFlowNod'))
        else if (nextLineArr.length === 1) {
          this.$emit('delNode', node.id)
          if (node.nodeName === this.$t('processDialog.nodeName.start'))
            this.$emit('updateMenuList', true)
          this.jsPlumb.remove(`${node.id}`)
          prevLineArr.some(res => {
            this.prevLineObj = res
            res.toId = nextLineArr[0]
            this.jsPlumb.connect({
              id: res.id,
              source: `${res.fromId}`,
              target: `${res.toId}`,
              overlays: [
                ["Label", { label: res.remark, cssClass: "myLabel", id: `${res.id}`, location: 0.5 }]
              ]
            }, this.jsplumbConnectOptions)
          })
        } else this.$nextTick(function () {
          this.jsPlumb.remove(`${node.id}`)
          if (node.nodeName === this.$t('processDialog.nodeName.start'))
            this.$emit('updateMenuList', true)
          this.$emit('delNode', node.id)
        })
      }).catch(() => {
      })
      return true
    },
    // 添加新的节点
    addNode (node) {
      let nodeId = `${new Date().getTime()}`
      this.option.nodeList.push({
        id: nodeId,
        ...node
      })
      this.$nextTick(function () {
        this.$emit('addNode', {
          id: nodeId,
          ...node
        })
        this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions)
        this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions)
        this.jsPlumb.draggable(nodeId, { containment: 'parent', grid: [1, 1] })
      })
    },
    // 更新节点
    updateNode (node) {
      for (let i = 0; i < this.option.nodeList.length; i++) {
        if (`${this.option.nodeList[i].id}` === `${node.id}`) {
          for (const k in this.option.nodeList[i]) {
            this.$set(this.option.nodeList[i], k, node[k])
            if (this.nodeMaintain && node.nodeType !== 4 && node.nodeType !== 6) {
              this.option.nodeList[i].assignedTo = node.assignedTo.join(',')
              this.option.nodeList[i].candidate = node.candidate.join(',')
            }
          }
          break
        }
      }
      this.$nextTick(function () {
        this.jsPlumb.remove('1')
      })
    },
    // 更新连接线
    updateLine (line) {
      this.option.lineList.some((res, index) => {
        line.some(res2 => {
          if (`${res.fromId}` === `${res2.fromId}` && `${res.toId}` === `${res2.toId}` && res2.isBackLine !== 1) {
            for (const k in res2) {
              if (k === 'objStatusTo' || k === 'condition' || k === 'backendTask') res2[k] = JSON.stringify(res2[k])
              this.$set(this.option.lineList[index], k, res2[k])
            }
            this.jsPlumb.getConnections().some(res3 => {
              if (res3.targetId === `${res2.toId}` && res3.sourceId === `${res2.fromId}`) {
                if (res3.getOverlay(`${res2.id}`) !== undefined) {
                  res3.getOverlay(`${res2.id}`).setLabel(res2.remark)
                }
                else {
                  this.loadEasyFlowFinish = false
                  this.jsPlumb.deleteConnection(res3)
                  this.jsPlumb.connect({
                    source: `${res2.fromId}`,
                    target: `${res2.toId}`,
                    overlays: [
                      ["Label", { label: res2.remark, cssClass: "myLabel", id: `${res2.id}`, location: 0.5 }]
                    ]
                  }, this.jsplumbConnectOptions)
                  this.option.lineList[index] = {
                    ...res2,
                    objStatusTo: JSON.stringify(res2.objStatusTo),
                    condition: JSON.stringify(res2.condition)
                  }
                  // this.option.lineList.push({
                  //   ...res2,
                  //   objStatusTo: JSON.stringify(res2.objStatusTo),
                  //   condition: JSON.stringify(res2.condition)
                  // })
                  res3.elementId = res2.id
                  this.loadEasyFlowFinish = true
                }
                return true
              }
            })
          } else if (res2.isBackLine === 1 && `${res.fromId}` === `${res2.fromId}`) {
            if (res2.deleteLine) { // 取消回退线
              this.jsPlumb.getConnections().some(res3 => {
                if (res3.sourceId === `${res2.fromId}` && res3.elementId === `${res2.id}`) {
                  this.jsPlumb.deleteConnection(res3)
                  return true
                }
              })
              if (!res2.continueLine) return
            }
            let flag = true
            this.option.lineList.some(res => {
              if (`${res.id}` === `${res2.id}`) {
                flag = false
                this.jsPlumb.getConnections().some(res3 => {
                  if (res3.targetId === `${res2.toId}` && res3.sourceId === `${res2.fromId}`) {
                    if (res3.getOverlay(`${res2.id}`) !== undefined)
                      res3.getOverlay(`${res2.id}`).setLabel(res2.remark)
                    else {
                      this.loadEasyFlowFinish = false
                      this.jsPlumb.deleteConnection(res3)
                      this.jsPlumb.connect({
                        source: `${res2.fromId}`,
                        target: `${res2.toId}`,
                        paintStyle: { stroke: 'lightgray', strokeWidth: 1 },
                        overlays: [
                          ["Label", { label: res2.remark, cssClass: "myLabel back-line", id: `${res2.id}`, location: 0.5 }]
                        ]
                      }, this.jsplumbConnectOptions)
                      this.option.lineList.push({
                        fromId: res2.fromId,
                        id: res2.id,
                        objStatusTo: JSON.stringify(res2.objStatusTo),
                        condition: JSON.stringify(res2.condition),
                        orgId: res2.orgId,
                        remark: res2.remark,
                        tempId: res2.tempId,
                        toId: res2.toId,
                        wfStatusTo: res2.wfStatusTo,
                        isBackLine: 1
                      })
                      res3.elementId = res2.id
                      this.loadEasyFlowFinish = true
                    }
                    return true
                  }
                })
                return true
              }
            })
            if (flag) {
              this.loadEasyFlowFinish = false
              this.jsPlumb.connect({
                source: `${res2.fromId}`,
                target: `${res2.toId}`,
                paintStyle: { stroke: 'lightgray', strokeWidth: 1 },
                overlays: [
                  ["Label", { label: res2.remark, cssClass: "myLabel back-line", id: `${res2.id}`, location: 0.5 }]
                ]
              }, this.jsplumbConnectOptions)
              this.option.lineList.push({
                fromId: res2.fromId,
                id: res2.id,
                objStatusTo: JSON.stringify(res2.objStatusTo),
                condition: JSON.stringify(res2.condition),
                orgId: res2.orgId,
                remark: res2.remark,
                tempId: res2.tempId,
                toId: res2.toId,
                wfStatusTo: res2.wfStatusTo,
                isBackLine: 1
              })
              this.jsPlumb.getConnections().some(res3 => {
                if (res3.sourceId === `${res2.fromId}` && res3.targetId === `${res2.toId}`) {
                  res3.elementId = `${res2.id}`
                  return true
                }
              })
              this.loadEasyFlowFinish = true
            }
          }
        })
      })
    },
    // 视图加载
    loadView (nodeList, lineList) {
      this.loadEasyFlowFinish = false
      this.$nextTick(function () {
        if (this.jsPlumb.getConnections().length > 0)
          this.jsPlumb.getConnections().some(res => {
            this.jsPlumb.deleteConnection(res)
          })
        if (this.isDrag) {
          // 加载节点
          nodeList.some(res => {
            // 设置源点，可以拖出线连接其他节点
            this.jsPlumb.makeSource(`${res.id}`, this.jsplumbSourceOptions)
            this.jsPlumb.makeTarget(`${res.id}`, this.jsplumbTargetOptions)
            this.jsPlumb.draggable(`${res.id}`, { containment: 'parent', grid: [1, 1] })
          })
        }
        // 加载连线
        lineList.some(res => {
          if (res.isBackLine === 1)
            this.jsPlumb.connect({
              id: `${res.id}`,
              source: `${res.fromId}`,
              target: `${res.toId}`,
              paintStyle: { stroke: 'lightgray', strokeWidth: 1 },
              overlays: [
                ["Label", { label: res.remark, cssClass: "myLabel back-line", id: `${res.id}`, location: 0.5 }]
              ]
            }, this.jsplumbConnectOptions)
          else
            this.jsPlumb.connect({
              id: `${res.id}`,
              source: `${res.fromId}`,
              target: `${res.toId}`,
              overlays: [
                ["Label", { label: res.remark, cssClass: "myLabel", id: `${res.id}`, location: 0.5 }]
              ]
            }, this.jsplumbConnectOptions)
          this.jsPlumb.getConnections().some(res2 => {
            if (res2.sourceId === `${res.fromId}` && res2.targetId === `${res.toId}`) {
              res2.elementId = `${res.id}`
              return true
            }
          })
        })
      })
      // 初始化连线
      this.$nextTick(function () {
        this.loadEasyFlowFinish = true
      })
    },
    loadEasyFlow () {
      if (this.isDrag) {
        // 初始化节点
        this.option.nodeList.some(res => {
          // 设置源点，可以拖出线连接其他节点
          this.jsPlumb.makeSource(`${res.id}`, this.jsplumbSourceOptions)
          // // 设置目标点，其他源点拖出的线可以连接该节点
          this.jsPlumb.makeTarget(`${res.id}`, this.jsplumbTargetOptions)
          this.jsPlumb.draggable(`${res.id}`, { containment: 'parent', grid: [1, 1] })
        })
      }
      this.option.lineList.some(res => {
        this.jsPlumb.connect({
          id: `${res.id}`,
          source: `${res.fromId}`,
          target: `${res.toId}`,
          overlays: [
            ["Label", { label: res.remark, cssClass: "myLabel", id: `${res.id}`, location: 0.5 }]
          ]
        }, this.jsplumbConnectOptions)
        this.jsPlumb.getConnections().some(res2 => {
          if (res2.sourceId === `${res.fromId}` && res2.targetId === `${res.toId}`) {
            res2.elementId = `${res.id}`
            return true
          }
        })
      })
    },
    jsPlumbInit () {
      const _this = this
      this.jsPlumb.ready(function () {
        // 导入默认配置
        _this.jsPlumb.importDefaults(_this.jsplumbSetting)
        // 会使整个jsPlumb立即重绘。
        _this.jsPlumb.setSuspendDrawing(false, true)
        // 初始化节点
        _this.loadEasyFlow()

        // 单点击了连接线,
        _this.jsPlumb.bind('click', function (conn, originalEvent) {
          // console.log('click', conn)
          if (_this.simple) return
          _this.$confirm(_this.$t('processDialog.isDeleteLine'), _this.$t('processDialog.tips'), {
            confirmButtonText: _this.$t('submitText'),
            cancelButtonText: _this.$t('cancelText'),
            type: 'warning'
          }).then(() => {
            _this.jsPlumb.deleteConnection(conn)
          }).catch(() => {
          })
        })
        // 连线
        _this.jsPlumb.bind('connection', function (evt) {
          // console.log('connection', evt)
          let from = evt.source.id
          let to = evt.target.id
          if (_this.loadEasyFlowFinish) {
            let index = 0
            _this.option.lineList.some(res => {
              if (`${res.fromId}` === from) res.remark = res.remark || _this.$t('processDialog.unnamed') + (++index)
            })
            _this.option.nodeList.some(res => {
              if (res.id === Number(from)) res.lineDisabled = false
            })
            let id = ''
            if (_this.prevLineObj.id) {
              id = _this.prevLineObj.id
              _this.option.lineList.push(_this.prevLineObj)
              _this.prevLineObj = {}
            } else {
              id = `${new Date().getTime()}`
              _this.option.lineList.push({
                id: id,
                fromId: from,
                toId: to,
                orgId: _this.orgId,
                isBackLine: 0,
                remark: index === 0 ? '' : _this.$t('processDialog.unnamed') + (++index)
              })
            }
            _this.getConnections().some(res => {
              if (res.sourceId === `${from}` && res.targetId === `${to}`) {
                res.elementId = id
                return true
              }
            })
          }
        })

        // 删除连线
        _this.jsPlumb.bind('connectionDetached', function (evt) {
          // console.log('connectionDetached', evt)
          _this.deleteLine(evt.sourceId, evt.targetId, evt.connection.elementId)
          _this.option.nodeList.some(res => {
            if (res.id === Number(evt.sourceId)) res.lineDisabled = false
          })
        })

        // 改变线的连接节点
        _this.jsPlumb.bind('connectionMoved', function (evt) {
          // console.log('connectionMoved', evt)
          _this.changeLine(evt.originalSourceId, evt.originalTargetId)
        })

        // 单击endpoint
        // jsPlumb.bind("endpointClick", function (evt) {
        //   console.log('endpointClick', evt)
        // })
        //
        // // 双击endpoint
        // jsPlumb.bind("endpointDblClick", function (evt) {
        //   console.log('endpointDblClick', evt)
        // })

        // contextmenu
        _this.jsPlumb.bind('contextmenu', function (evt) {
          // console.log('contextmenu', evt)
        })

        // beforeDrop
        _this.jsPlumb.bind('beforeDrop', function (evt) {
          // console.log('beforeDrop', evt)
          let from = evt.sourceId
          let to = evt.targetId
          let endPointFlag = false
          let index = 0
          let startNode = {}
          let countersign = {} // 会签节点
          let countersignFlag = false
          _this.loopFlag = false
          _this.option.nodeList.some(res => {
            if (`${res.id}` === from && res.nodeName === _this.$t('processDialog.nodeName.end')) endPointFlag = true
            if (res.nodeName === _this.$t('processDialog.nodeName.start')) startNode = res
            if (res.nodeType === 4 && `${res.id}` === from) countersign = res
          })
          if (from === `${startNode.id}`) index++
          _this.option.lineList.some(res => {
            if (`${res.fromId}` === `${startNode.id}`) index++
            if (`${res.fromId}` === `${countersign.id}` && res.isBackLine !== 1) countersignFlag = true
          })
          if (countersignFlag) {
            _this.$message.error(_this.$t('processDialog.countersignNodeOnlyDirection'))
            return false
          }
          if (endPointFlag) {
            _this.$message.error(_this.$t('processDialog.cannotEndToStart'))
            return false
          }
          if (index > 1) {
            _this.$message.error(_this.$t('processDialog.startNodeOneLine'))
            return false
          }
          if (evt.dropEndpoint.element.innerText === _this.$t('processDialog.nodeName.start')) {
            _this.$message.error(_this.$t('processDialog.cannotConnectStartNode'))
            return false
          }
          if (from === to) {
            _this.$message.error(_this.$t('processDialog.cannotConnectOwn'))
            return false
          }
          if (_this.hasLine(from, to)) {
            _this.$message.error(_this.$t('processDialog.cannotRepeatConnection'))
            return false
          }
          _this.hashOppositeLine(to, from)
          if (_this.loopFlag) {
            _this.$message.error(_this.$t('processDialog.cannotGoBack'))
            return false
          }
          if (_this.option.nodeList.filter(res => { return res.id === Number(from) })[0].isAddable !== 1 && _this.nodeMaintain) {
            _this.$message.warning(_this.$t('processDialog.noPermissionAdd'))
            return false
          }
          return true
        })

        // beforeDetach
        _this.jsPlumb.bind('beforeDetach', function (evt) {
          // console.log('beforeDetach', evt)
        })
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.right {
  position: relative;
  background: #f1f5f8;
  overflow: hidden;
}
.myLabel {
  margin-left: 20px;
  margin-top: 20px;
}
// .back-line {
// }
</style>